<template>
    <div class="live-player-container" :class="{ 'has-error': !!errorMessage }">
      <!-- 一个 video，内部根据协议选择原生 / hls.js / flv.js -->
      <video
        ref="videoRef"
        class="native-player"
        :controls="controls"
        playsinline
        :muted="muted"
        @playing="handlePlaying"
        @error="handleHtmlError"
        @canplay="handleCanPlay"
      />
  
      <!-- 状态覆盖层（不拦截鼠标事件） -->
      <div v-if="!isPlaying || isLoading || errorMessage" class="status-overlay">
        <span>{{ statusText }}</span>
      </div>
    </div>
  </template>
  
  <script setup>
  import {
    ref,
    watch,
    onMounted,
    onBeforeUnmount,
    computed,
    nextTick
  } from 'vue'
  import Hls from 'hls.js'
  import flvjs from 'flv.js'
  
  const props = defineProps({
    /**
     * 播放地址：
     * - mp4: https://xxx/trailer.mp4
     * - hls: https://xxx/live.m3u8 
     * - flv: https://xxx/live.flv
     * - rtmp: rtmp://...
     */
    src: { type: String, required: true },
  
    /**
     * 协议类型：
     *  - auto: 自动根据链接后缀/协议猜测
     *  - mp4 / webm / ogg
     *  - hls
     *  - flv
     *  - rtmp（只能提示，不可真正播放）
     */
    protocol: {
      type: String,
      default: 'auto',
      validator: val =>
        ['auto', 'mp4', 'webm', 'ogg', 'hls', 'flv', 'rtmp'].includes(val)
    },
  
    autoplay: { type: Boolean, default: true },
    controls: { type: Boolean, default: true },
  
    // 是否静音（静音更容易通过浏览器的自动播放限制）
    muted: { type: Boolean, default: true }
  })
  
  const emit = defineEmits(['ready', 'error', 'playing'])
  
  const videoRef = ref(null)
  const hlsInstance = ref(null)
  const flvInstance = ref(null)
  
  const isPlaying = ref(false)
  const isLoading = ref(false)
  const errorMessage = ref('')
  
  const statusText = computed(() => {
    if (errorMessage.value) return errorMessage.value
    if (isLoading.value) return '正在连接直播流...'
    if (isPlaying.value) return '点击播放'
    return '等待播放'
  })
  
  /** 自动根据 src 推断协议 */
  function detectProtocolFromSrc(src) {
    if (!src) return 'mp4'
    const lower = src.split('?')[0].toLowerCase()
    if (lower.startsWith('rtmp://')) return 'rtmp'
    if (lower.endsWith('.m3u8')) return 'hls'
    if (lower.endsWith('.flv')) return 'flv'
    if (lower.endsWith('.webm')) return 'webm'
    if (lower.endsWith('.ogg') || lower.endsWith('.ogv')) return 'ogg'
    if (lower.endsWith('.mp4')) return 'mp4'
    return 'mp4'
  }
  
  /** 是否支持原生 HLS（主要是 Safari） */
  function supportsNativeHls(videoEl) {
    if (!videoEl || typeof videoEl.canPlayType !== 'function') return false
    return (
      videoEl.canPlayType('application/vnd.apple.mpegurl') ||
      videoEl.canPlayType('application/x-mpegURL')
    )
  }
  
  /** 初始化播放器核心逻辑 */
  async function setupPlayer() {
    const video = videoRef.value
    if (!video || !props.src) return
  
    cleanup()
  
    isPlaying.value = false
    isLoading.value = true
    errorMessage.value = ''
  
    const proto =
      props.protocol === 'auto'
        ? detectProtocolFromSrc(props.src)
        : props.protocol
  
    // console.log('use protocol:', proto)
  
    switch (proto) {
      case 'flv':
        initFlv()
        break
      case 'hls':
        initHls()
        break
      case 'mp4':
      case 'webm':
      case 'ogg':
        initNative()
        break
      case 'rtmp':
        handleError(
          '浏览器不支持直接播放 RTMP，请使用转码后的 HLS/FLV/WebRTC 地址'
        )
        break
      default:
        // 其他一律让浏览器自己尝试
        initNative()
    }
  }
  
  /** 原生播放（mp4/webm/ogg/浏览器能识别的都可以让它试一试） */
  function initNative() {
    const video = videoRef.value
    if (!video) return
    video.src = props.src
    video.load()
  
    if (props.autoplay) {
      video
        .play()
        .then(() => {
          // 自动播放成功会触发 playing 事件
        })
        .catch(() => {
          // 自动播放被拦截，保持 overlay 提示“点击播放”
          isLoading.value = false
        })
    } else {
      isLoading.value = false
    }
  }
  
  /** HLS 播放：先试原生，失败再用 hls.js */
  function initHls() {
    const video = videoRef.value
    if (!video) return
  
    if (supportsNativeHls(video)) {
      // Safari / 一些原生支持 HLS 的浏览器
      initNative()
      return
    }
  
    if (!Hls.isSupported()) {
      handleError('当前浏览器不支持 HLS 播放')
      return
    }
  
    const hls = new Hls({
      enableWorker: true,
      lowLatencyMode: true
    })
    hlsInstance.value = hls
  
    hls.loadSource(props.src)
    hls.attachMedia(video)
  
    hls.on(Hls.Events.MANIFEST_PARSED, () => {
      if (props.autoplay) {
        video
          .play()
          .then(() => {
            // ok
          })
          .catch(() => {
            isLoading.value = false
          })
      } else {
        isLoading.value = false
      }
    })
  
    hls.on(Hls.Events.ERROR, (_, data) => {
      console.warn('HLS error', data)
      if (data?.fatal) {
        handleError('HLS 播放错误：' + (data?.details || '未知错误'))
      }
    })
  }
  
  /** FLV 播放：用 flv.js */
  function initFlv() {
    const video = videoRef.value
    if (!video) return
  
    if (!flvjs.isSupported()) {
      handleError('当前浏览器不支持 FLV 播放')
      return
    }
  
    const flv = flvjs.createPlayer({
      type: 'flv',
      url: props.src,
      isLive: true
    })
    flvInstance.value = flv
  
    flv.attachMediaElement(video)
    flv.load()
  
    if (props.autoplay) {
      flv
        .play()
        .then(() => {})
        .catch(() => {
          isLoading.value = false
        })
    } else {
      isLoading.value = false
    }
  
    flv.on(flvjs.Events.ERROR, (errType, errDetail) => {
      handleError(`FLV 播放错误: ${errType} - ${errDetail}`)
    })
  }
  
  /** video 原生事件：开始播放 */
  function handlePlaying() {
    isPlaying.value = true
    isLoading.value = false
    emit('playing')
  }
  
  /** video 原生事件：可以播放了（但还没 necessarily 开始） */
  function handleCanPlay() {
    // 如果没自动播放成功，这个回调至少可以把“加载中”去掉
    if (!isPlaying.value) {
      isLoading.value = false
    }
  }
  
  /** 原生 video 错误 */
  function handleHtmlError() {
    const video = videoRef.value
    const err = video?.error
    if (!err) {
      handleError('未知播放错误')
      return
    }
    const map = {
      1: '视频加载中断',
      2: '网络错误',
      3: '视频解码错误',
      4: '视频格式不支持'
    }
    handleError(map[err.code] || '未知播放错误')
  }
  
  /** 统一错误处理 */
  function handleError(msg) {
    console.error('[LivePlayer error]', msg)
    errorMessage.value = msg
    isPlaying.value = false
    isLoading.value = false
    emit('error', msg)
  }
  
  /** 清理所有内核实例 + 重置 video */
  function cleanup() {
    const video = videoRef.value
    if (hlsInstance.value) {
      hlsInstance.value.destroy()
      hlsInstance.value = null
    }
    if (flvInstance.value) {
      try {
        flvInstance.value.pause()
        flvInstance.value.unload()
        flvInstance.value.detachMediaElement()
        flvInstance.value.destroy()
      } catch (e) {
        // ignore
      }
      flvInstance.value = null
    }
    if (video) {
      video.pause()
      video.removeAttribute('src')
      video.load()
    }
  }
  
  /** 监听地址 / 协议变化，重新初始化播放器 */
  watch(
    () => [props.src, props.protocol],
    async () => {
      await nextTick()
      await setupPlayer()
    }
  )
  
  onMounted(async () => {
    await nextTick()
    await setupPlayer()
  })
  
  onBeforeUnmount(() => {
    cleanup()
  })
  </script>
  
  <style scoped>
  .live-player-container {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    background-color: #000;
  }
  
  .has-error {
    border: 1px solid var(--el-color-danger, #f56c6c);
  }
  
  .native-player {
    width: 100%;
    height: 100%;
    object-fit: contain;
  }
  
  /* 状态覆盖层不拦截鼠标，用户仍然可以点击 video */
  .status-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background: radial-gradient(circle at center, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.85));
    color: #fff;
    z-index: 1;
    font-size: 13px;
    pointer-events: none;
  }
  </style>
  